#include "StdAfx.h"
#include "WebConn.h"

BOOL CWebHttpConn::SecurityRandom(LPVOID lpBuffer, DWORD dwLen) const
{ 
	HCRYPTPROV hCryptProv;
	BOOL bRet = FALSE;

	if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_SILENT))
	{
		if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_SILENT))
		{
			return bRet;
		}
	}

	if(hCryptProv == NULL)
	{
		return bRet;
	}

	bRet = CryptGenRandom(hCryptProv, dwLen, (LPBYTE)lpBuffer);

	CryptReleaseContext(hCryptProv, 0);

	return bRet;
}

CString CWebHttpConn::GenerateBoundary() const
{
	CString csBoundary;
	unsigned __int64 u64Random;
	
	if (!SecurityRandom(&u64Random, sizeof(u64Random)))
	{
		AfxMessageBox(_T("Error gen random"));
	}
	
	csBoundary.Format(_T("---------------------------%016I64x"), u64Random);

	return csBoundary;
}

CStringA CWebHttpConn::MakePreFileData(LPCTSTR lpszBoundary, LPCTSTR lpszFieldName, LPCTSTR lpszFileName) const
{
	CString csData;

	csData.Format(_T("--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\nContent-Type: application/octet-stream\r\n\r\n"), lpszBoundary, lpszFieldName, lpszFileName);

	return T2UTF8(csData);
}

CStringA CWebHttpConn::MakePostFileData(LPCTSTR lpszBoundary) const
{
	CString csData;

	csData.Format(_T("\r\n--%s--\r\n"), lpszBoundary);

	return T2UTF8(csData);
}

BOOL CWebHttpConn::HttpSetHost(LPCTSTR lpszHttpHostname, WORD wPort)
{
	if (m_csHostname.IsEmpty() && lpszHttpHostname != NULL && _tcslen(lpszHttpHostname) > 0)
	{
		m_csHostname = lpszHttpHostname;
		m_Port = wPort;

		return TRUE;
	}

	return FALSE;
}

void CWebHttpConn::HttpSetHeader(LPCTSTR lpszHeaders)
{
	TCHAR chLastChar;

	m_csHttpHeaders = lpszHeaders;
	if (m_csHttpHeaders.GetLength() == 0)
	{
		return;
	}

	chLastChar = m_csHttpHeaders.GetAt(m_csHttpHeaders.GetLength()-1);
	while (chLastChar == _T('\r') || chLastChar == _T('\n') || chLastChar == _T('\t') || chLastChar == _T(' '))
	{
		m_csHttpHeaders = m_csHttpHeaders.Left(m_csHttpHeaders.GetLength()-1);
		if (m_csHttpHeaders.GetLength() == 0)
		{
			break;
		}
	};
}

const CString& CWebHttpConn::HttpGetHeader()
{
	return m_csHttpHeaders;
}

void CWebHttpConn::HttpAddHeader(LPCTSTR lpszHeaders)
{
	if (_tcslen(lpszHeaders) == 0)
	{
		return;
	}

	if (m_csHttpHeaders.GetLength() > 0)
	{
		m_csHttpHeaders += _T("\r\n");
	}
	m_csHttpHeaders += lpszHeaders;
}

BOOL CWebHttpConn::HttpGetString(LPCTSTR lpszUri, CString& csRet)
{
	CHttpConnection* pHttpConn = NULL;
	CHttpFile* pHttpFile = NULL;
	DWORD dwStatusCode;
	DWORD dwLen;
	LPSTR lpszBuf = NULL;
	DWORD dwReadBytes;

	try
	{
		pHttpConn = m_HttpSession.GetHttpConnection(m_csHostname, m_Port);
		if (pHttpConn == NULL)
		{
			throw 0;
		}

		pHttpFile = pHttpConn->OpenRequest(CHttpConnection::HTTP_VERB_GET, lpszUri);
		if (pHttpFile == NULL)
		{
			throw 0;
		}

		if (!pHttpFile->SendRequest(m_csHttpHeaders))
		{
			throw 0;
		}

		if (!pHttpFile->QueryInfoStatusCode(dwStatusCode))
		{
			throw 0;
		}

		if (dwStatusCode != HTTP_STATUS_OK)
		{
			throw 0;
		}

		try
		{
			dwLen = (DWORD)pHttpFile->SeekToEnd();
			if (dwLen == 0)
			{
				throw 0;
			}
			pHttpFile->SeekToBegin();
		}
		catch (CInternetException* e)
		{
			e->Delete();
			dwLen = (DWORD)pHttpFile->GetLength();
		}
		catch (...)
		{
			dwLen = (DWORD)pHttpFile->GetLength();
		}

		lpszBuf = (LPSTR)malloc(dwLen+1);
		if (lpszBuf == NULL)
		{
			throw 0;
		}

		dwReadBytes = pHttpFile->Read(lpszBuf, dwLen);
		if (dwReadBytes != dwLen)
		{
			throw 0;
		}

		lpszBuf[dwLen] = '\0';
		csRet = UTF82T(lpszBuf);

		if (lpszBuf != NULL)
		{
			free(lpszBuf);
			lpszBuf = NULL;
		}

		if (pHttpFile != NULL)
		{
			pHttpFile->Close();
			delete pHttpFile;
			pHttpFile = NULL;
		}

		if (pHttpConn != NULL)
		{
			pHttpConn->Close();
			delete pHttpConn;
			pHttpConn = NULL;
		}

		return TRUE;
	}
	catch (...)
	{
		if (lpszBuf != NULL)
		{
			free(lpszBuf);
			lpszBuf = NULL;
		}

		if (pHttpFile != NULL)
		{
			delete pHttpFile;
			pHttpFile = NULL;
		}

		if (pHttpFile != NULL)
		{
			pHttpFile->Close();
			delete pHttpFile;
			pHttpFile = NULL;
		}

		if (pHttpConn != NULL)
		{
			pHttpConn->Close();
			delete pHttpConn;
			pHttpConn = NULL;
		}
	}

	return FALSE;
}

BOOL CWebHttpConn::HttpGetToFile(LPCTSTR lpszUri, LPCTSTR lpszFilePathName)
{
	CHttpConnection* pHttpConn = NULL;
	CHttpFile* pHttpFile = NULL;
	DWORD dwStatusCode;
	DWORD dwLen;
	BYTE  arrBuf[0x1000];
	DWORD dwReadBytes;
	CFile file;

	USES_CONVERSION;

	try
	{
		if (!file.Open(lpszFilePathName, CFile::modeWrite|CFile::typeBinary|CFile::modeCreate))
		{
			throw 0;
		}

		pHttpConn = m_HttpSession.GetHttpConnection(m_csHostname, m_Port);
		if (pHttpConn == NULL)
		{
			throw 0;
		}

		pHttpFile = pHttpConn->OpenRequest(CHttpConnection::HTTP_VERB_GET, lpszUri);
		if (pHttpFile == NULL)
		{
			throw 0;
		}

		if (!pHttpFile->SendRequest(m_csHttpHeaders))
		{
			throw 0;
		}

		if (!pHttpFile->QueryInfoStatusCode(dwStatusCode))
		{
			throw 0;
		}

		if (dwStatusCode != HTTP_STATUS_OK)
		{
			throw 0;
		}

		try
		{
			dwLen = (DWORD)pHttpFile->SeekToEnd();
			if (dwLen == 0)
			{
				throw 0;
			}
			pHttpFile->SeekToBegin();
		}
		catch (CInternetException* e)
		{
			e->Delete();
			dwLen = (DWORD)pHttpFile->GetLength();
		}
		catch (...)
		{
			throw 0;
		}

		pHttpFile->SeekToBegin();
		while ((dwReadBytes = pHttpFile->Read(arrBuf, min(sizeof(arrBuf), dwLen))) != 0)
		{
			file.Write(arrBuf, dwReadBytes);
			dwLen -= dwReadBytes;
			if (dwLen == 0)
			{
				break;
			}
		}

		if (dwLen != 0)
		{
			throw 0;
		}

		file.Close();

		if (pHttpFile != NULL)
		{
			pHttpFile->Close();
			delete pHttpFile;
			pHttpFile = NULL;
		}

		if (pHttpConn != NULL)
		{
			pHttpConn->Close();
			delete pHttpConn;
			pHttpConn = NULL;
		}
		return TRUE;
	}
	catch (...)
	{
		if (file.GetStatus(CFileStatus()))
		{
			file.Close();
		}

		if (CFileFind().FindFile(lpszFilePathName))
		{
			DeleteFile(lpszFilePathName);
		}

		if (pHttpFile != NULL)
		{
			pHttpFile->Close();
			delete pHttpFile;
			pHttpFile = NULL;
		}

		if (pHttpConn != NULL)
		{
			pHttpConn->Close();
			delete pHttpConn;
			pHttpConn = NULL;
		}
	}

	return FALSE;
}

BOOL CWebHttpConn::HttpPostString(LPCTSTR lpszUri, LPCTSTR lpszPostData, CString& csRet)
{
	CHttpConnection* pHttpConn = NULL;
	CHttpFile* pHttpFile = NULL;
	DWORD dwStatusCode;
	DWORD dwLen;
	LPSTR lpszBuf = NULL;
	DWORD dwReadBytes;
	LPCSTR lpszEncodePostString;
	CString csHttpHeaders;

	USES_CONVERSION;

	try
	{
		pHttpConn = m_HttpSession.GetHttpConnection(m_csHostname, m_Port);
		if (pHttpConn == NULL)
		{
			throw 0;
		}

		pHttpFile = pHttpConn->OpenRequest(CHttpConnection::HTTP_VERB_POST, lpszUri);
		if (pHttpFile == NULL)
		{
			throw 0;
		}

		csHttpHeaders = m_csHttpHeaders;
		if (csHttpHeaders.GetLength() > 0)
		{
			csHttpHeaders += _T("\r\n");
		}
		csHttpHeaders += _T("Content-Type: application/x-www-form-urlencoded; charset=UTF-8");
		lpszEncodePostString = T2A(UrlEncode(lpszPostData));
		if (!pHttpFile->SendRequest(csHttpHeaders, (LPVOID)lpszEncodePostString, strlen(lpszEncodePostString)))
		{
			throw 0;
		}

		if (!pHttpFile->QueryInfoStatusCode(dwStatusCode))
		{
			throw 0;
		}

		if (dwStatusCode != HTTP_STATUS_OK)
		{
			throw 0;
		}

		try
		{
			dwLen = (DWORD)pHttpFile->SeekToEnd();
			if (dwLen == 0)
			{
				throw 0;
			}
			pHttpFile->SeekToBegin();
		}
		catch (CInternetException* e)
		{
			e->Delete();
			dwLen = (DWORD)pHttpFile->GetLength();
		}
		catch (...)
		{
			dwLen = (DWORD)pHttpFile->GetLength();
		}

		lpszBuf = (LPSTR)malloc(dwLen+1);
		if (lpszBuf == NULL)
		{
			throw 0;
		}

		dwReadBytes = pHttpFile->Read(lpszBuf, dwLen);
		if (dwReadBytes != dwLen)
		{
			throw 0;
		}

		lpszBuf[dwLen] = '\0';
		csRet = UTF82T(lpszBuf);

		if (lpszBuf != NULL)
		{
			free(lpszBuf);
			lpszBuf = NULL;
		}

		if (pHttpFile != NULL)
		{
			pHttpFile->Close();
			delete pHttpFile;
			pHttpFile = NULL;
		}

		if (pHttpConn != NULL)
		{
			pHttpConn->Close();
			delete pHttpConn;
			pHttpConn = NULL;
		}

		return TRUE;
	}
	catch (...)
	{
		if (lpszBuf != NULL)
		{
			free(lpszBuf);
			lpszBuf = NULL;
		}

		if (pHttpFile != NULL)
		{
			pHttpFile->Close();
			delete pHttpFile;
			pHttpFile = NULL;
		}

		if (pHttpConn != NULL)
		{
			pHttpConn->Close();
			delete pHttpConn;
			pHttpConn = NULL;
		}
	}

	return FALSE;
}

BOOL CWebHttpConn::HttpPostFile(LPCTSTR lpszUri, LPCTSTR lpszFieldName, LPCTSTR lpszFilePathName, LPCTSTR lpszRemoteFileName, CString& csRet)
{
	CHttpConnection* pHttpConn = NULL;
	CHttpFile* pHttpFile = NULL;
	DWORD dwStatusCode;
	DWORD dwLen;
	LPSTR lpszBuf = NULL;
	CFile file;
	CString csBoundary;
	DWORD dwFileLen;
	CStringA csPreFileData;
	CStringA csPostFileData;
	BYTE arrBuffer[0x1000];
	DWORD dwReadBytes;;
	DWORD dwTotalReadBytes;
	CString csHttpHeaders;

	USES_CONVERSION;

	try
	{
		csBoundary = GenerateBoundary();

		if (!file.Open(lpszFilePathName, CFile::modeRead | CFile::typeBinary))
		{
			throw 0;
		}
		
		dwFileLen = (DWORD)file.GetLength();

		csPreFileData = MakePreFileData(csBoundary, lpszFieldName, lpszRemoteFileName);
		csPostFileData = MakePostFileData(csBoundary);

		pHttpConn = m_HttpSession.GetHttpConnection(m_csHostname, m_Port);
		if (pHttpConn == NULL)
		{
			throw 0;
		}

		pHttpFile = pHttpConn->OpenRequest(CHttpConnection::HTTP_VERB_POST, lpszUri);
		if (pHttpFile == NULL)
		{
			throw 0;
		}

		csHttpHeaders = m_csHttpHeaders;
		if (csHttpHeaders.GetLength() > 0)
		{
			csHttpHeaders += _T("\r\n");
		}
		csHttpHeaders += _T("Content-Type: multipart/form-data; boundary=") + csBoundary;
		pHttpFile->AddRequestHeaders(csHttpHeaders);

		pHttpFile->SendRequestEx(csPreFileData.GetLength()+dwFileLen+csPostFileData.GetLength());

		pHttpFile->Write((LPCSTR)csPreFileData, csPreFileData.GetLength());
		dwTotalReadBytes = 0;
		dwReadBytes = min(sizeof(arrBuffer),(dwFileLen-dwTotalReadBytes));
		while ((dwReadBytes = file.Read(arrBuffer, dwReadBytes)) > 0)
		{
			pHttpFile->Write(arrBuffer, dwReadBytes);
			dwTotalReadBytes += dwReadBytes;
			dwReadBytes = min(sizeof(arrBuffer),(dwFileLen-dwTotalReadBytes));

			if (dwTotalReadBytes == dwFileLen)
			{
				break;
			}
		}
		if (dwTotalReadBytes != dwFileLen)
		{
			throw 0;
		}
		pHttpFile->Write((LPCSTR)csPostFileData, csPostFileData.GetLength());

		pHttpFile->EndRequest();

		if (!pHttpFile->QueryInfoStatusCode(dwStatusCode))
		{
			throw 0;
		}

		if (dwStatusCode != HTTP_STATUS_OK)
		{
			throw 0;
		}

		try
		{
			dwLen = (DWORD)pHttpFile->SeekToEnd();
			if (dwLen == 0)
			{
				throw 0;
			}
			pHttpFile->SeekToBegin();
		}
		catch (CInternetException* e)
		{
			e->Delete();
			dwLen = (DWORD)pHttpFile->GetLength();
		}
		catch (...)
		{
			dwLen = (DWORD)pHttpFile->GetLength();
		}

		lpszBuf = (LPSTR)malloc(dwLen+1);
		if (lpszBuf == NULL)
		{
			throw 0;
		}

		dwReadBytes = pHttpFile->Read(lpszBuf, dwLen);
		if (dwReadBytes != dwLen)
		{
			throw 0;
		}

		lpszBuf[dwLen] = '\0';
		csRet = UTF82T(lpszBuf);

		if (lpszBuf != NULL)
		{
			free(lpszBuf);
			lpszBuf = NULL;
		}

		if (pHttpFile != NULL)
		{
			pHttpFile->Close();
			delete pHttpFile;
			pHttpFile = NULL;
		}

		if (pHttpConn != NULL)
		{
			pHttpConn->Close();
			delete pHttpConn;
			pHttpConn = NULL;
		}

		return TRUE;
	}
	catch (...)
	{
		if (lpszBuf != NULL)
		{
			free(lpszBuf);
			lpszBuf = NULL;
		}

		if (pHttpFile != NULL)
		{
			pHttpFile->Close();
			delete pHttpFile;
			pHttpFile = NULL;
		}

		if (pHttpConn != NULL)
		{
			pHttpConn->Close();
			delete pHttpConn;
			pHttpConn = NULL;
		}
	}

	return FALSE;
}